﻿using System.IO.Ports;
using Digitalmes.Core.Common.SerialPorts;
using Digitalmes.Core.Configuration;

namespace Digitalmes.Infrastructure.IO.SerialPorts;

/// <summary>
/// 端口提供者。
/// </summary>
/// <param name="publisher"></param>
/// <param name="logger"></param>
internal sealed class SerialPortsProvider(IPublisher publisher, ILogger<SerialPortsProvider> logger) : ISerialPortsProvider
{
    private readonly List<RsSerialPort> _serialPorts = [];

    /// <summary>
    /// 获取所有注册的串口对象。
    /// </summary>
    public IReadOnlyList<IRsSerialPort> SerialPorts => _serialPorts;

    /// <summary>
    /// 加载配置的串口。
    /// </summary>
    public async Task LoadAsync(CancellationToken cancellationToken = default)
    {
        var options = await AssetsConfigHelper.GetAsync<List<SerialPortOptions>>(AssetsConstants.SerialPortsFileName, cancellationToken);
        if (options == null)
        {
            return;
        }

        var localPortNames = SerialPort.GetPortNames();
        foreach (var option in options)
        {
            if(!localPortNames.Contains(option.PortName))
            {
                logger.LogWarning("本地没有找到要注册的串口 {PortName}", option.PortName);
                continue;
            }

            SerialPort serialPort = new(option.PortName, option.BaudRate, option.Parity, option.DataBits, option.StopBits)
            {
                Encoding = Encoding.ASCII,
            };
            if (option.ReadBufferSize is not null)
            {
                serialPort.ReadBufferSize = option.ReadBufferSize.Value;
            }
            if (option.WriteBufferSize is not null)
            {
                serialPort.WriteBufferSize = option.WriteBufferSize.Value;
            }
            if (option.ReceivedBytesThreshold is not null)
            {
                serialPort.ReceivedBytesThreshold = option.ReceivedBytesThreshold.Value;
            }
            if (option.ReadTimeout is not null)
            {
                serialPort.ReadTimeout = option.ReadTimeout.Value;
            }
            if (option.WriteTimeout is not null)
            {
                serialPort.WriteTimeout = option.WriteTimeout.Value;
            }

            serialPort.DataReceived += async (object sender, SerialDataReceivedEventArgs e) =>
            {
                var serialPort = (SerialPort)sender;
                var content = serialPort.ReadExisting();

                if (!string.IsNullOrEmpty(content))
                {
                    Debug.WriteLine(content);
                    logger.LogInformation("串口 {PortName} 接收数据: {Content}", serialPort.PortName, content);

                    await publisher.Publish(new SerialPortDataReceivedEvent(new RsSerialPort(serialPort), content)).ConfigureAwait(false);
                }
            };
            serialPort.ErrorReceived += async (object sender, SerialErrorReceivedEventArgs e) =>
            {
                var serialPort = (SerialPort)sender;

                Debug.WriteLine(e.ToString());
                logger.LogInformation("串口 {PortName} 接收数据错误: {Error}", serialPort.PortName, e.ToString());

                await publisher.Publish(new SerialPortErrorReceivedEvent(new RsSerialPort(serialPort), e.EventType.ToString())).ConfigureAwait(false);
            };

            var rsSerialPort = new RsSerialPort(serialPort)
            {
                Purpose = option.Purpose,
            };
            _serialPorts.Add(rsSerialPort);

            logger.LogInformation("已注册串口 {PortName}", option.PortName);

            if (option.AllowAutoOpen)
            {
                try
                {
                    // 打开串口
                    rsSerialPort.Open();
                    logger.LogInformation("已打开串口 {PortName}，串口参数：{@Option}", option.PortName, option);
                }
                catch (Exception ex)
                {
                    logger.LogWarning("串口 {PortName} 打开异常，错误：{Message}，串口参数：{@Option}", option.PortName, ex.Message, option);
                }
            }
        }
    }

    /// <summary>
    /// 释放并清除所有的已注册的串口。
    /// </summary>
    public void Clear()
    {
        _serialPorts.ForEach(serialPort => serialPort.Dispose());
        _serialPorts.Clear();
    }
}
